package main

import (
	"fmt"           // 用于格式化和打印输出
	"os"            // 用于文件系统操作（读文件、写文件、创建目录、获取命令行参数）
	"path/filepath" // 用于处理文件路径（获取目录名、拼接路径）
	"regexp"        // 用于正则表达式匹配
	"strings"       // 用于字符串处理（去除空格、换行符）
)

// 定义一个正则表达式来匹配标记行，例如 "文件 1: `index.html`"
// 它会捕获括号中的文件名部分
var fileMarkerRegex = regexp.MustCompile(`文件\s*\d+:\s*` + "`" + `([^` + "`" + `]+)` + "`")

// 定义一个正则表达式来匹配代码块，包括开始和结束的 ```
// 使用 (?s) 标志让 . 可以匹配换行符
// 使用 .*? 实现非贪婪匹配，找到最近的结束 ```
var codeBlockRegex = regexp.MustCompile("(?s)```" + `(?:[^\n]*)` + `\n(.*?)\n` + "```")

func main() {
	// --- 1. 获取并检查命令行参数 ---
	// os.Args[0] 是程序本身的名称
	// os.Args[1] 应该是我们想要处理的 Markdown 文件名
	if len(os.Args) < 2 {
		fmt.Println("错误：请提供 Markdown 文件的路径作为命令行参数。")
		fmt.Println("用法：go run script.go <markdown_file.md>")
		os.Exit(1) // 参数不足，异常退出
	}
	markdownFilePath := os.Args[1] // 获取用户提供的文件名

	// --- 2. 读取 Markdown 文件内容 ---
	fmt.Printf("正在读取 Markdown 文件: %s\n", markdownFilePath)
	contentBytes, err := os.ReadFile(markdownFilePath)
	if err != nil {
		fmt.Printf("错误：无法读取文件 '%s': %v\n", markdownFilePath, err)
		os.Exit(1) // 读取失败，异常退出
	}
	content := string(contentBytes) // 将文件内容字节转换为字符串

	// --- 3. 查找所有文件标记 ---
	// FindAllStringSubmatchIndex 返回所有匹配项及其子匹配项（捕获组）在原字符串中的索引
	// 每个元素是 []int{ matchStart, matchEnd, group1Start, group1End, ... }
	markerIndices := fileMarkerRegex.FindAllStringSubmatchIndex(content, -1)
	if markerIndices == nil {
		fmt.Println("错误：在文件中未找到任何 '文件 X: `filename`' 格式的标记。")
		os.Exit(1) // 未找到标记，异常退出
	}

	fmt.Printf("找到了 %d 个文件标记，开始处理...\n", len(markerIndices))

	// --- 4. 遍历标记并提取/写入文件 ---
	for i, markerIndex := range markerIndices {
		// 提取文件名 (捕获组 1 的内容)
		filename := content[markerIndex[2]:markerIndex[3]]
		filename = strings.TrimSpace(filename) // 去除可能存在的前后空格

		if filename == "" {
			fmt.Printf("警告：在第 %d 个标记处找到空文件名，跳过。\n", i+1)
			continue
		}

		// 确定当前标记对应内容的搜索范围
		// 开始位置是当前标记的结束位置
		searchStart := markerIndex[1]
		// 结束位置是下一个标记的开始位置，或者是整个文件内容的末尾
		searchEnd := len(content)
		if i+1 < len(markerIndices) {
			searchEnd = markerIndices[i+1][0] // 下一个标记的开始索引
		}

		// 在当前标记和下一个标记之间查找第一个代码块
		segmentToSearch := content[searchStart:searchEnd]
		codeMatch := codeBlockRegex.FindStringSubmatch(segmentToSearch)

		if codeMatch == nil || len(codeMatch) < 2 {
			fmt.Printf("警告：未能在标记 '%s' 后找到有效的代码块，跳过。\n", filename)
			continue // 没找到代码块，处理下一个标记
		}

		// 提取代码内容 (捕获组 1 的内容)
		codeContent := codeMatch[1]
		// 去除代码块内容前后可能的多余空行或空格
		codeContent = strings.TrimSpace(codeContent)

		// --- 5. 创建目录并写入文件 ---
		fmt.Printf("  -> 正在处理: %s\n", filename)

		// 获取文件所在的目录路径
		dirPath := filepath.Dir(filename)

		// 如果目录路径不是当前目录 ".", 则创建它
		// os.MkdirAll 会创建所有必需的父目录，并且如果目录已存在也不会报错
		if dirPath != "." {
			fmt.Printf("     创建目录 (如果需要): %s\n", dirPath)
			err := os.MkdirAll(dirPath, 0755) // 0755 是常用的目录权限
			if err != nil {
				fmt.Printf("错误：无法创建目录 '%s': %v\n", dirPath, err)
				continue // 创建目录失败，处理下一个标记
			}
		}

		// 将提取的代码内容写入文件
		// os.WriteFile 会自动处理文件创建和覆盖
		fmt.Printf("     写入文件: %s\n", filename)
		err := os.WriteFile(filename, []byte(codeContent), 0644) // 0644 是常用的文件权限
		if err != nil {
			fmt.Printf("错误：无法写入文件 '%s': %v\n", filename, err)
			continue // 写入文件失败，处理下一个标记
		}
	}

	fmt.Println("\n处理完成！")
}
